package org.fixies.spreadsheet;

import org.fixies.resource.BookLoader;
import org.fixies.resource.ResourceLoader;
import org.fixies.spreadsheet.interpreter.BookContext;
import org.fixies.utils.ClassUtils;
import org.fixies.utils.FIFOMap;

import java.util.Map;
import java.util.NoSuchElementException;

import static org.fixies.utils.Validator.notNull;

public abstract class AbstractBookLoader implements BookLoader {

    private static Map<String, Book> cache__;

    private Class<?> clazz_;

    private String fName_;

    private ResourceLoader resourceLoader_;

    private final BookContext bookContext_;

    static {
        initialize();
    }

    private static void initialize() {
        cache__ = new FIFOMap<String, Book>(32);
    }


    public AbstractBookLoader(Class<?> clazz, BookContext bookContext) {
        this(clazz, getCallingTestMethodName(), bookContext);
    }

    public AbstractBookLoader(Class<?> clazz, String fName, BookContext bookContext) {
        clazz_ = clazz;
        fName_ = fName;
        bookContext_ = bookContext;
    }

    @Override
    public Book getBook(String bookName) {
        Book book = cache__.get(bookName);
        if (book == null) {
            book = doGet(bookName);
            cache__.put(bookName, book);
        }
        return book;
    }

    private Book doGet(String bookName) {
        Book book = loadBook(bookName);
        if (book == null) {
            throw new NoSuchElementException("Book ["
                    + bookName
                    + "] not found");
        }
        return book;
    }

    @Override
    public Book getBook() {
        String bookName = getDefaultBookName();
        return getBook(bookName);
    }

    @Override
    public Sheet getSheet(String sheetName) {
        Book book = getBook();
        Sheet sheet = book.getSheet(sheetName);
        if (sheet == null) {
            throw new NoSuchElementException("Sheet ["
                    + sheetName
                    + "] not found."
                    + "Book =[" + getDefaultBookName() + "]");
        }
        return sheet;

    }

    @Override
    public Sheet getSheet() {
        return getSheet(fName_);
    }

    @Override
    public Unit getUnit(String id) {
        return getSheet().getUnit(id);
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        notNull(resourceLoader);
        resourceLoader_ = resourceLoader;
    }

    private String getDefaultBookName() {
        return ClassUtils.getResourceName(clazz_);
    }

    private static String getCallingTestMethodName() {
        return ClassUtils.getCallingTestMethodName(new Throwable()
                .getStackTrace());
    }


    protected final ResourceLoader getResourceLoader() {
        notNull(resourceLoader_);
        return resourceLoader_;
    }

    protected final BookContext getBookContext() {
        return bookContext_;
    }


    protected abstract Book loadBook(String path);
}
